//
// Created by yyl on 2018/6/6.
//


#include "YAudio.h"

YAudio::YAudio(PlayStatus *playStatus, int sample_rate, CallJava *callJava) {
    this->callJava = callJava;
    this->playStatus = playStatus;
    this->sample_rate = sample_rate;
    this->queue = new YQueue(this->playStatus);
    this->buffer = static_cast<uint8_t *>(av_malloc(sample_rate * 2 * 2));
    this->sampletypeBuffer = static_cast<SAMPLETYPE *>(malloc(sample_rate * 2 * 2));
    this->soundTouch = new SoundTouch();
    this->soundTouch->setSampleRate(static_cast<uint>(sample_rate));
    this->soundTouch->setChannels(2);
    this->soundTouch->setPitch(mPitch);//音调1.5倍
    this->soundTouch->setTempo(mSpeed);
    pthread_mutex_init(&codecMutexLock, NULL);
}

YAudio::~YAudio() {
    //   sample_rate = 0;
    pthread_mutex_destroy(&codecMutexLock);
}

void *decodePlay(void *data) {
    YAudio *audio = static_cast<YAudio *>(data);

//    audio->resampleAudio();
    audio->initOpenSLES();

    pthread_exit(&audio->threadPlay);
}

void YAudio::play() {
    pthread_create(&threadPlay, NULL, decodePlay, this);
}

//FILE *outFile=fopen("/mnt/sdcard/0/demo.pcm","w");


int YAudio::resampleAudio(void **pcmbuf) {
    data_size = 0;
    readFrameFinished = true;
    while (playStatus != NULL && !playStatus->exit) {
        if (playStatus->seek) {
            av_usleep(1000 * 10);//10毫秒
            continue;
        }
        if (queue->getQueueSize() == 0) {//音频播放中 队列没有数据就等待
            if (!playStatus->load) {
                playStatus->load = true;
                if (callJava != NULL)
                    callJava->onCallBackEvent(THREAD_CHILD, EVENT_STATE_PLAY, false, 0);
            }
            av_usleep(1000 * 10);//10毫秒
            continue;
        } else {
            if (playStatus != NULL && playStatus->load) {
                playStatus->load = false;
                if (callJava != NULL)
                    callJava->onCallBackEvent(THREAD_CHILD, EVENT_STATE_PLAY, EVENT_FALSE, 0);
            }
        }

        if (readFrameFinished) {//当avFrame文读取完之后才读取下一个
            avPacket = av_packet_alloc();//分配内存
            if (queue->getAvpacket(avPacket) != 0) {
                av_packet_free(&avPacket);//这里只会释放avPacket *data 以外的引用 因为 *data 被另一个 packet 引用了
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
            pthread_mutex_lock(&codecMutexLock);
            result = avcodec_send_packet(avCodecContext, avPacket);//
            if (result != 0) {
                av_packet_free(&avPacket);//这里只会释放avPacket *data 以外的引用 因为 *data 被另一个 packet 引用了
                av_free(avPacket);
                avPacket = NULL;
                pthread_mutex_unlock(&codecMutexLock);
                continue;
            }
            pthread_mutex_unlock(&codecMutexLock);
        }
        avFrame = av_frame_alloc();//分配内存
        pthread_mutex_lock(&codecMutexLock);
        result = avcodec_receive_frame(avCodecContext, avFrame);
        if (result == 0) {
            readFrameFinished = false;
            if (avFrame->channels && avFrame->channel_layout == 0) {
                avFrame->channel_layout = av_get_default_channel_layout(avFrame->channels);

            } else if (avFrame->channels == 0 && avFrame->channel_layout > 0) {
                avFrame->channels = av_get_channel_layout_nb_channels(
                        avFrame->channel_layout);
            }

            SwrContext *swr_ctx = NULL;
            swr_ctx = swr_alloc_set_opts(
                    NULL,
                    AV_CH_LAYOUT_SURROUND,
                    AV_SAMPLE_FMT_S16,
                    avFrame->sample_rate,
                    avFrame->channel_layout,
                    static_cast<AVSampleFormat>(avFrame->format),
                    avFrame->sample_rate,
                    NULL,
                    NULL);
            if (!swr_ctx || swr_init(swr_ctx) < 0) {
                av_packet_free(&avPacket);//这里只会释放avPacket *data 以外的引用 因为 *data 被另一个 packet 引用了
                av_free(avPacket);
                avPacket = NULL;
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;
                if (swr_ctx != NULL) {
                    swr_free(&swr_ctx);
                }
                readFrameFinished = true;
                pthread_mutex_unlock(&codecMutexLock);
                continue;
            }
            channelnb = swr_convert(
                    swr_ctx,
                    &buffer,
                    avFrame->nb_samples,
                    (const uint8_t **) (avFrame->data),
                    avFrame->nb_samples);

            int out_channels = av_get_channel_layout_nb_channels(AV_CH_LAYOUT_STEREO);
            data_size = channelnb * out_channels * av_get_bytes_per_sample(AV_SAMPLE_FMT_S16);
            //  LOGI("data size is %d", data_size);
            *pcmbuf = buffer;

            now_time = avFrame->pts * av_q2d(time_base);
           LOGI("buffer  data_size=%d  now_time=%lf  clock=%lf  ", data_size, now_time, clock);
            if (now_time < clock) {
                now_time = clock;
            }
            clock = now_time;

            //    fwrite(buffer,1,data_size,outFile); //保存成本地文件  写入文件
//            av_packet_free(&avPacket);//这里只会释放avPacket *data 以外的引用 因为 *data 被另一个 packet 引用了
//            av_free(avPacket);
//            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
            if (swr_ctx != NULL) {
                swr_free(&swr_ctx);
                swr_ctx = NULL;
            }
            pthread_mutex_unlock(&codecMutexLock);
            break;
        } else {
            readFrameFinished = true;
            av_packet_free(&avPacket);//这里只会释放avPacket *data 以外的引用 因为 *data 被另一个 packet 引用了
            av_free(avPacket);
            avPacket = NULL;
            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;
        }
        pthread_mutex_unlock(&codecMutexLock);
    }
    return data_size;
}

int YAudio::getSoundTouchData() {

    while (playStatus != NULL && !playStatus->exit) {
        out_buffer = NULL;
        if (isFinished) {
            isFinished = false;

            int buffersize = resampleAudio(reinterpret_cast<void **>(&out_buffer));
            if (buffersize > 0) {//这里要从8位转换为16位  因为pcm是8位 soundtouch最低要16位
                for (int i = 0; i < buffersize / 2 + 1; i++) {
                    sampletypeBuffer[i] = (out_buffer[i * 2] | ((out_buffer[i * 2 + 1]) << 8));
                }
                soundTouch->putSamples(sampletypeBuffer, channelnb);
                numCount = soundTouch->receiveSamples(sampletypeBuffer, buffersize / 4);//采样个数
            } else {
                soundTouch->flush();
            }
        }
        if (numCount == 0) {
            isFinished = true;
            continue;
        } else {
            if (out_buffer == NULL) {
                numCount = soundTouch->receiveSamples(sampletypeBuffer, data_size / 4);//采样个数
                if (numCount == 0) {
                    isFinished = true;
                    continue;
                }
            }
            return numCount;
        }


    }
    return 0;
}

void pcmBufferCallBack(SLAndroidSimpleBufferQueueItf bf, void *context) {
    YAudio *audio = (YAudio *) context;
    if (audio != NULL) {
        //    int bufferSize = audio->resampleAudio(reinterpret_cast<void **>(&audio->out_buffer));
        int bufferSize = audio->getSoundTouchData();
        if (bufferSize > 0) {
            audio->clock += bufferSize / ((double) audio->sample_rate * 2 * 2);
            if (audio->clock - audio->last_time >= 0.1) {
                audio->last_time = audio->clock;
                audio->callJava->onCallBackEvent(THREAD_CHILD, EVENT_STATE_TIME, audio->duration,
                                                 audio->clock);
            }
            if (audio->isRecord)
                audio->callJava->onCallPcmToAAC(THREAD_CHILD, EVENT_STATE_PCM_BUFFER,
                                                bufferSize * 2 * 2, audio->sampletypeBuffer);

            int dbValue = audio->getPCMDB(reinterpret_cast<char *>(audio->sampletypeBuffer),
                                          bufferSize * 4);
            audio->callJava->onCallBackEvent(THREAD_CHILD, EVENT_STATE_PCM_DB, dbValue, 0);

//            (*audio->pcmBufferQueue)->Enqueue(audio->pcmBufferQueue, audio->buffer, bufferSize);
            (*audio->pcmBufferQueue)->Enqueue(audio->pcmBufferQueue,
                                              (char *) audio->sampletypeBuffer,
                                              bufferSize * 2 * 2);
        }
    }


}

int YAudio::changePlayState(bool isPlay) {
    if (pcmPlayerObject != NULL) {
        if (isPlay) {
            (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);
        } else {
            (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PAUSED);
        }
        LOGI("changePlayState  isPlay=%d", isPlay);
        return 0;
    }
    return -1;
}

int YAudio::getCurrentSampleRateForOpenSLES(int sample_rate) {
    int rate = 0;
    switch (sample_rate) {
        case 8000:
            rate = SL_SAMPLINGRATE_8;
            break;
        case 11025:
            rate = SL_SAMPLINGRATE_11_025;
            break;
        case 12000:
            rate = SL_SAMPLINGRATE_12;
            break;
        case 16000:
            rate = SL_SAMPLINGRATE_16;
            break;
        case 22050:
            rate = SL_SAMPLINGRATE_22_05;
            break;
        case 24000:
            rate = SL_SAMPLINGRATE_24;
            break;
        case 32000:
            rate = SL_SAMPLINGRATE_32;
            break;
        case 44100:
            rate = SL_SAMPLINGRATE_44_1;
            break;
        case 48000:
            rate = SL_SAMPLINGRATE_48;
            break;
        case 64000:
            rate = SL_SAMPLINGRATE_64;
            break;
        case 88200:
            rate = SL_SAMPLINGRATE_88_2;
            break;
        case 96000:
            rate = SL_SAMPLINGRATE_96;
            break;
        case 192000:
            rate = SL_SAMPLINGRATE_192;
            break;
        default:
            rate = SL_SAMPLINGRATE_44_1;
    }
    return rate;
}

void YAudio::initOpenSLES() {

    SLresult result;
    //第一步------------------------------------------
    // 创建引擎对象
    result = slCreateEngine(&engineObject, 0, 0, 0, 0, 0);
    result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
    result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);


    //第二步-------------------------------------------
    // 创建混音器
    const SLInterfaceID mids[1] = {SL_IID_ENVIRONMENTALREVERB};
    const SLboolean mreq[1] = {SL_BOOLEAN_FALSE};
    result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, mids, mreq);
    (void) result;
    result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
    (void) result;
    result = (*outputMixObject)->GetInterface(outputMixObject, SL_IID_ENVIRONMENTALREVERB,
                                              &outputMixEnvironmentalReverb);
    if (SL_RESULT_SUCCESS == result) {
        result = (*outputMixEnvironmentalReverb)->SetEnvironmentalReverbProperties(
                outputMixEnvironmentalReverb, &reverbSettings);
        (void) result;
    }
    SLDataLocator_OutputMix outputMix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
    SLDataSink audioSnk = {&outputMix, 0};




    // 第三步------配置PCM格式信息--------------------------------------
    // 创建播放器
    SLDataLocator_AndroidSimpleBufferQueue android_queue = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,
                                                            2};
    SLDataFormat_PCM pcm = {
            SL_DATAFORMAT_PCM,//播放pcm格式的数据
            2,//2个声道（立体声）
            static_cast<SLuint32>(getCurrentSampleRateForOpenSLES(sample_rate)),//44100hz的频率
            SL_PCMSAMPLEFORMAT_FIXED_16,//位数 16位
            SL_PCMSAMPLEFORMAT_FIXED_16,//和位数一致就行
            SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,//立体声（前左前右）
            SL_BYTEORDER_LITTLEENDIAN//结束标志
    };
    SLDataSource slDataSource = {&android_queue, &pcm};

    const SLInterfaceID ids[4] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME,
                                  SL_IID_MUTESOLO, SL_IID_PLAYBACKRATE};
    const SLboolean req[4] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE,
                              SL_BOOLEAN_TRUE};
    //SL_IID_BUFFERQUEUE  声音缓冲队列
    //SL_IID_EFFECTSEND
    //SL_IID_VOLUME  音量
    //SL_IID_MUTESOLO  左右声道
    //SL_IID_PLAYBACKRATE
    (*engineEngine)->CreateAudioPlayer(engineEngine, &pcmPlayerObject, &slDataSource,
                                       &audioSnk, 4, ids, req);
    // 初始化播放器
    (*pcmPlayerObject)->Realize(pcmPlayerObject, SL_BOOLEAN_FALSE);

    //得到接口后调用  获取Player接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_PLAY, &pcmPlayerPlay);
    //获取音量接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_VOLUME, &pcmVolumePlay);
    //获取左右声道接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_MUTESOLO, &pcmMuteSolo);

    // 获取缓冲区接口
    (*pcmPlayerObject)->GetInterface(pcmPlayerObject, SL_IID_BUFFERQUEUE, &pcmBufferQueue);
    //缓冲接口回调
    (*pcmBufferQueue)->RegisterCallback(pcmBufferQueue, pcmBufferCallBack, this);

    // 设置播放状态
    (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_PLAYING);

    // 主动调用回调函数开始工作
    pcmBufferCallBack(pcmBufferQueue, this);

    //  setVolume(volumePercent);
}


void YAudio::setVolume(int percent) {
    this->volumePercent = percent;
    if (pcmVolumePlay != NULL) {
        if (percent > 30) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -20);
        } else if (percent > 25) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -22);
        } else if (percent > 20) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -25);
        } else if (percent > 15) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -28);
        } else if (percent > 10) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -30);
        } else if (percent > 5) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -34);
        } else if (percent > 3) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -37);
        } else if (percent > 0) {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -40);
        } else {
            (*pcmVolumePlay)->SetVolumeLevel(pcmVolumePlay, (100 - percent) * -100);
        }
    }

}

void YAudio::setMute(int mute) {
    this->muteType = mute;
    if (pcmMuteSolo != NULL) {
        switch (mute) {
            case 0://right
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 1, false);// 左声道关闭
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 0, true);//  右声道打开
                break;
            case 1://left
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 1, true);//  左声道打开
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 0, false);//  右声道关闭
                break;
            case 2://center 立体声
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 1, false);//1左声道关闭
                (*pcmMuteSolo)->SetChannelMute(pcmMuteSolo, 0, false);//0右声道关闭
                break;
        }
    }
}

void YAudio::stop() {
    if (pcmPlayerPlay != NULL) {
        LOGI("YAudio  stop");
        (*pcmPlayerPlay)->SetPlayState(pcmPlayerPlay, SL_PLAYSTATE_STOPPED);
    }

}

void YAudio::release() {
    stop();
    if (queue != NULL) {
        queue->release();
    }
    pthread_join(threadPlay, NULL);
    if (queue != NULL) {
        delete (queue);
        queue = NULL;
    }
    if (pcmPlayerObject != NULL) {
        (*pcmPlayerObject)->Destroy(pcmPlayerObject);
        pcmPlayerObject = NULL;
        pcmPlayerPlay = NULL;
        pcmBufferQueue = NULL;
        pcmMuteSolo = NULL;
        pcmVolumePlay = NULL;

    }
    if (outputMixObject != NULL) {
        (*outputMixObject)->Destroy(outputMixObject);
        outputMixObject = NULL;
        outputMixEnvironmentalReverb = NULL;
    }
    if (engineObject != NULL) {
        (*engineObject)->Destroy(engineObject);
        engineObject = NULL;
        engineEngine = NULL;
    }
    if (buffer != NULL) {
        free(buffer);
        buffer = NULL;
    }
    if (out_buffer != NULL) {
        out_buffer = NULL;
    }
    if (soundTouch == NULL) {
        delete soundTouch;
        soundTouch = NULL;
    }
    if (sampletypeBuffer != NULL) {
        free(sampletypeBuffer);
        sampletypeBuffer = NULL;
    }
    if (avCodecContext != NULL) {
        pthread_mutex_lock(&codecMutexLock);
        avcodec_close(avCodecContext);
        avcodec_free_context(&avCodecContext);
        avCodecContext = NULL;
        pthread_mutex_unlock(&codecMutexLock);
    }
    if (playStatus != NULL)
        playStatus = NULL;
    if (callJava != NULL)
        callJava = NULL;
    LOGI("YAudio::release");
}

void YAudio::setSpeed(double speed) {
    this->mSpeed = speed;
    if (soundTouch != NULL) {
        soundTouch->setTempo(speed);
    }


}

void YAudio::setPitch(double pitch) {
    this->mPitch = pitch;
    if (soundTouch != NULL) {
        soundTouch->setPitch(pitch);
    }
}

int YAudio::getPCMDB(char *pcmcata, size_t pcmsize) {
    int db = 0;
    short int pervalue = 0;
    double sum = 0;
    for (int i = 0; i < pcmsize; i += 2) {
        memcpy(&pervalue, pcmcata + i, 2);
        sum += abs(pervalue);
    }
    sum = sum / (pcmsize / 2);
    if (sum > 0) {
        db = (int) 20.0 * log10(sum);
    }
    return db;
}





